package com.study.zookeeper;

import com.alibaba.fastjson.JSONObject;
import com.study.common.annotation.MyService;
import com.study.common.base.RegisterInfo;
import com.study.common.register.IServiceRegister;
import com.study.common.utils.NetAddressUtil;
import com.study.connect.service.IStartService;
import com.study.zookeeper.thread.ServiceRunThread;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationListener;
import org.springframework.context.event.ContextRefreshedEvent;
import org.springframework.stereotype.Component;

import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.Map;

/**
 * @包名称 com.study.zookeeper
 * @类描述 <pre></pre>
 * @作者 zhangbo
 * @创建时间 2020/5/31.
 * @版本 1.00
 * @修改记录 <pre>
 *     版本           修改人         修改日期         修改内容描述
 *     -----------------------------------------------------------
 *     1.00           zhangbo        2020/5/31.           类创建
 *     -----------------------------------------------------------
 * </pre>
 */
@Slf4j
@Component
public class MyServiceContextListener implements ApplicationListener<ContextRefreshedEvent> {

    @Autowired
    private IServiceRegister serviceRegister;

    @Autowired
    private IStartService startService;

    @Override
    public void onApplicationEvent(ContextRefreshedEvent contextRefreshedEvent) {
        log.info("开始加载服务注册者对象");
        if (contextRefreshedEvent.getApplicationContext().getParent() == null) {
            /*根容器为spring*/
            Map<String, Object> beansWithAnnotation = contextRefreshedEvent.getApplicationContext().getBeansWithAnnotation(MyService.class);
            if (null != beansWithAnnotation.values() && beansWithAnnotation.values().size() > 0) {
                String serviceIp = NetAddressUtil.getServerIp();
                final int servicePort = NetAddressUtil.getPort();
                log.info("需要进行注册绑定，获取本机ip地址：{}，端口号：{}", serviceIp, servicePort);
                startService(servicePort);
                for (Object service : beansWithAnnotation.values()) {
                    Class<?>[] interfaces = service.getClass().getInterfaces();
                    /*构建服务注册对象*/
                    MyService annotation = service.getClass().getAnnotation(MyService.class);
                    RegisterInfo registerInfo = getRegisterInfo(annotation);
                    registerInfo.setServiceIp(serviceIp);
                    registerInfo.setServicePort(servicePort);
                    if (interfaces != null && interfaces.length > 0) {
                    /*通过接口注册服务*/
                        for (Class clazz : interfaces) {
                            if (checkRegisterInterface(clazz, service.getClass())) {
                                /*检测为是服务实现匹配类，进行服务注册*/
                                Method[] methods = clazz.getDeclaredMethods();
                                getRegisterMethods(registerInfo, methods);
                                registerService(clazz.getName(), registerInfo);
                            }
                        }
                    } else {
                        /*直接使用类注册服务*/
                        Method[] methods = service.getClass().getDeclaredMethods();
                        getRegisterMethods(registerInfo, methods);
                        registerService(service.getClass().getName(), registerInfo);
                    }
                }
            }
        }
        log.info("服务注册者对象加载完成");
    }

    /**
     * 获取服务类需要进行注册的方法
     *
     * @param registerInfo
     * @param methods
     */
    private void getRegisterMethods(RegisterInfo registerInfo, Method[] methods) {
        if (null != methods && methods.length > 0) {
            registerInfo.setMethodList(new ArrayList<>(methods.length));
            for (Method method : methods) {
                /*如果方法为public方法，则加入注册列表中*/
                if (Modifier.isPublic(method.getModifiers())) {
                    registerInfo.getMethodList().add(method.getName());
                }
            }
        }
    }

    /**
     * 校验是否为注册服务的接口：
     * 校验规则为包名匹配（掊包名全匹配实现类包名）
     *
     * @param interfaces 接口
     * @param clazz      实现类
     * @return
     */
    private boolean checkRegisterInterface(Class interfaces, Class clazz) {
        boolean isImpl = clazz.getPackage().getName().contains(interfaces.getPackage().getName());
        log.info("包名：{}，实现类：{}，注册标识：{}", interfaces.getName(), clazz.getName(), isImpl);
        return isImpl;
    }

    /**
     * 服务注册
     *
     * @param clazzName
     * @param registerInfo
     * @return
     */
    private boolean registerService(String clazzName, RegisterInfo registerInfo) {
        StringBuilder stringBuilder = new StringBuilder(clazzName);
        stringBuilder.append("/").append(registerInfo.getServiceIp()).append(":").append(registerInfo.getServicePort());
        log.info("注册目录：{},注册服务信息对象：{}", clazzName, registerInfo);
        /*此处可以进行优化，不使用json格式或对对象进行压缩*/
        String register = JSONObject.toJSONString(registerInfo);
        boolean isRegister = serviceRegister.register(stringBuilder.toString(), register.getBytes());
        if (!isRegister) {
            for(int i = 0 ; i < 3 ; i++) {
                log.info("服务注册失败开始重试：{}", clazzName);
                isRegister = serviceRegister.register(stringBuilder.toString(), register.getBytes());
                if (isRegister) {
                    log.info("服务注册成功");
                    return true;
                }
            }
            log.error("服务注册失败：{}",clazzName);
        }
        return false;
    }

    /**
     * 根据注解获取服务注册配置参数信息
     *
     * @return
     */
    private RegisterInfo getRegisterInfo(MyService annotation) {
        RegisterInfo registerInfo = new RegisterInfo();
        registerInfo.setVersion(annotation.version());
        registerInfo.setWeight(annotation.weight());
        registerInfo.setClientCount(0);
        registerInfo.setClientIpList(new HashSet<>());
        return registerInfo;
    }

    /**
     * 启动服务监听线程
     *
     * @param port
     */
    public void startService(Integer port) {
        ServiceRunThread serviceRunThread = new ServiceRunThread(this.startService, port);
        Thread startThread = new Thread(serviceRunThread);
        startThread.setDaemon(true);
        startThread.start();
    }

}
